﻿using System.Drawing;

namespace TextureGenerator.Mechanics
{
    public class Drawer
    {
        private Bitmap bitmap;
        private Color[] colors;
        private GenConfig config;
        public Drawer(GenConfig config)
        {
            this.config = config;
            int pixel = config.GetPixel();
            this.bitmap = new Bitmap(pixel, pixel);
            colors = new Color[8];
            colors[0] = Color.FromArgb(189, 216, 249);
            colors[1] = Color.FromArgb(209, 236, 249);
            colors[2] = Color.FromArgb(169, 224, 168);
            colors[3] = Color.FromArgb(186, 224, 168);
            colors[4] = Color.FromArgb(194, 224, 168);
            colors[5] = Color.FromArgb(218, 224, 168);
            colors[6] = Color.FromArgb(224, 212, 168);
            colors[7] = Color.FromArgb(224, 181, 168);
        }

        public Bitmap Draw(int[,]? depth)
        {
            int layer = 8;
            int side = config.Side;
            if (depth != null)
                side *= 4;
            int seed = config.Seed;
            float detail = config.Detail;
            int castType = config.ColorMode;

            int maxSide = (int)Math.Pow(2, layer);
            List<Perlin> perlins = new();
            for (int i = 0; i < layer; i++)
            {
                perlins.Add(new(side * seed, side));
                side *= 2;
                if (side > maxSide)
                {
                    break;
                }
            }
            int h = bitmap.Height;
            int w = bitmap.Width;
            float[,] height = new float[h, w];


            for (int y = 0; y < bitmap.Height; y++)
            {
                for (int x = 0; x < bitmap.Width; x++)
                {
                    float xratio = x / (float)bitmap.Width;
                    float yratio = y / (float)bitmap.Height;

                    float ratio = 1f;
                    if(depth!=null)
                        ratio = 0.1f;
                    for (int i = 0; i < perlins.Count; i++)
                    {
                        float res = perlins[i].GetHeight(xratio, yratio);
                        if (depth != null)
                            res -= 0.3f;

                        if (config.EnsureIsland && depth==null)
                            height[y, x] += EnsureIsland(xratio, yratio, res * ratio);
                        else 
                            height[y, x] += res * ratio;
                        ratio /= detail;
                    }
                    if (depth != null)
                        height[y,x] += (depth[x, y] / 512f);
                }
            }

            for (int y = 0; y < bitmap.Height; y++)
            {
                for (int x = 0; x < bitmap.Width; x++)
                {
                    float value = EnsurePlain(height[y, x], config.EnsurePlain);

                    value *= 2;

                    Color c;
                    if (castType == 0)
                    {
                        c = CastColorful(value);
                    }
                    else if (castType == 2)
                    {
                        c = CastHard(value);
                    }
                    else
                    {
                        c = CastSoft(value);
                    }
                    bitmap.SetPixel(x, y, c);
                }
            }
            return bitmap;
        }
        private static Color CastHard(float value)
        {
            if (value <= 0)
                return Color.FromArgb(209, 236, 249);
            else
                return Color.White;
        }
        private static Color CastSoft(float value)
        {
            if (value <= 0)
                return Color.White;
            else
            {
                int d = (int)(-value * 2 * 127.5 + 127.5);
                d = Math.Clamp(d, 0, 255);
                return Color.FromArgb(d, d, d);
            }
        }
        private Color CastColorful(float value)
        {
            if (value < -0.1f)
                return colors[0];
            if (value < 0)
                return colors[1];
            if (value < 0.05f)
                return colors[2];
            if (value < 0.07)
                return colors[3];
            if (value < 0.1)
                return colors[4];
            if (value < 0.2f)
                return colors[5];
            if (value < 0.3f)
                return colors[6];
            return colors[7];
        }

        private static float EnsureIsland(float xratio, float yratio, float value)
        {
            float r = MathF.Sqrt((xratio - 0.5f) * (xratio - 0.5f) + (yratio - 0.5f) * (yratio - 0.5f));
            float thd = 0.3f;
            if (r < thd)
            {
                return value;
            }
            else
            {
                return value - (r - thd) * (r - thd) * 2;
            }
        }
        private static float EnsurePlain(float value, float force)
        {
            if (value > 0.01f)
            {
                if (value > force/100f)
                {
                    return value - force/100f;
                }
                return 0.01f;
            }
            return value;
        }
    }
    public class GenConfig
    {
        public int ColorMode { get; set; }
        public bool EnsureIsland { get; set; }
        public float EnsurePlain { get; set; }
        public float Detail { get; set; }
        public int Side { get; set; }
        public int Seed { get; set; }
        public int Pixel { get; set; }
        public int GetPixel()
        {
            int pixel;
            if (this.Pixel == 0)
                pixel = 256;
            else if (this.Pixel == 1)
                pixel = 512;
            else
                pixel = 1024;
            return pixel;
        }
    }
}
